package ca.scotthyndman.as2haxe.output;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.log4j.Logger;

import ca.scotthyndman.as2haxe.Constants;
import ca.scotthyndman.as2haxe.dom.ASArrayAccess;
import ca.scotthyndman.as2haxe.dom.ASArrayLiteral;
import ca.scotthyndman.as2haxe.dom.ASAssignment;
import ca.scotthyndman.as2haxe.dom.ASBlock;
import ca.scotthyndman.as2haxe.dom.ASBooleanLiteral;
import ca.scotthyndman.as2haxe.dom.ASBreakStatement;
import ca.scotthyndman.as2haxe.dom.ASCatchStatement;
import ca.scotthyndman.as2haxe.dom.ASComment;
import ca.scotthyndman.as2haxe.dom.ASCompilationUnit;
import ca.scotthyndman.as2haxe.dom.ASConditionalExpression;
import ca.scotthyndman.as2haxe.dom.ASContinueStatement;
import ca.scotthyndman.as2haxe.dom.ASDoStatement;
import ca.scotthyndman.as2haxe.dom.ASExpressionStatement;
import ca.scotthyndman.as2haxe.dom.ASFieldAccess;
import ca.scotthyndman.as2haxe.dom.ASForInStatement;
import ca.scotthyndman.as2haxe.dom.ASForStatement;
import ca.scotthyndman.as2haxe.dom.ASFunction;
import ca.scotthyndman.as2haxe.dom.ASFunctionExpression;
import ca.scotthyndman.as2haxe.dom.ASIfStatement;
import ca.scotthyndman.as2haxe.dom.ASImportDeclaration;
import ca.scotthyndman.as2haxe.dom.ASInfixExpression;
import ca.scotthyndman.as2haxe.dom.ASInstanceOfExpression;
import ca.scotthyndman.as2haxe.dom.ASMethodInvocation;
import ca.scotthyndman.as2haxe.dom.ASModifier;
import ca.scotthyndman.as2haxe.dom.ASName;
import ca.scotthyndman.as2haxe.dom.ASNewExpression;
import ca.scotthyndman.as2haxe.dom.ASNullLiteral;
import ca.scotthyndman.as2haxe.dom.ASNumberLiteral;
import ca.scotthyndman.as2haxe.dom.ASObjectLiteral;
import ca.scotthyndman.as2haxe.dom.ASObjectLiteralField;
import ca.scotthyndman.as2haxe.dom.ASPackage;
import ca.scotthyndman.as2haxe.dom.ASPackageDeclaration;
import ca.scotthyndman.as2haxe.dom.ASParenthesizedExpression;
import ca.scotthyndman.as2haxe.dom.ASPostfixExpression;
import ca.scotthyndman.as2haxe.dom.ASPrefixExpression;
import ca.scotthyndman.as2haxe.dom.ASPropertyDeclaration;
import ca.scotthyndman.as2haxe.dom.ASQualifiedName;
import ca.scotthyndman.as2haxe.dom.ASReturnStatement;
import ca.scotthyndman.as2haxe.dom.ASSimpleName;
import ca.scotthyndman.as2haxe.dom.ASStringLiteral;
import ca.scotthyndman.as2haxe.dom.ASSwitchCase;
import ca.scotthyndman.as2haxe.dom.ASSwitchStatement;
import ca.scotthyndman.as2haxe.dom.AST;
import ca.scotthyndman.as2haxe.dom.ASTNode;
import ca.scotthyndman.as2haxe.dom.ASThrowStatement;
import ca.scotthyndman.as2haxe.dom.ASTryStatement;
import ca.scotthyndman.as2haxe.dom.ASType;
import ca.scotthyndman.as2haxe.dom.ASTypeDeclaration;
import ca.scotthyndman.as2haxe.dom.ASUndefinedLiteral;
import ca.scotthyndman.as2haxe.dom.ASVariableDeclaration;
import ca.scotthyndman.as2haxe.dom.ASVariableDeclarationExpression;
import ca.scotthyndman.as2haxe.dom.ASVariableDeclarationStatement;
import ca.scotthyndman.as2haxe.dom.ASWhileStatement;
import ca.scotthyndman.as2haxe.dom.ASWithStatement;
import ca.scotthyndman.as2haxe.dom.visitor.IVisitable;
import ca.scotthyndman.as2haxe.dom.visitor.IVisitor;
import ca.scotthyndman.as2haxe.output.FormattingOptions.BracePosition;

/**
 * Outputs haXe code given an AST.
 */
public class HaxeWriter implements IVisitor {

	private static final String GETTER_PREFIX = "__get__";

	private static final String SETTER_PREFIX = "__set__";

	private Logger log = Logger.getLogger(HaxeWriter.class);

	private File outputDir;

	private StringBuffer currentBuffer;

	private File currentFile;

	private int currentLinePosition = 0;

	private int currentLine = 0;

	private int tabDepth = 0;

	private FormattingOptions options = new FormattingOptions();

	private Map<ASPackage, File> packageDirs = new HashMap<ASPackage, File>();

	//
	// Construction
	//

	public HaxeWriter(String outputDir) {
		this.outputDir = new File(outputDir);
	}

	/**
	 * Performs the output.
	 */
	public void execute(AST ast) {
		ASPackage.getRootPackage().accept(this, true);
	}

	// ===== BUFFER STUFF

	public void print(String str) {
		if (currentLinePosition == 0) {
			for (int j = 0; j < tabDepth; j++) {
				currentBuffer.append('\t');
			}
		}

		String[] lines = str.split("\n");
		for (int i = 0; i < lines.length; i++) {
			currentBuffer.append(lines[i]);
			if (i + 1 != lines.length) {
				newline();
			}
		}

		int newlineIdx = str.lastIndexOf('\n');
		if (newlineIdx != -1) {
			currentLinePosition = str.length() - newlineIdx;
		} else {
			currentLinePosition += str.length();
		}
	}

	public void newline() {
		currentBuffer.append('\n');
		currentLinePosition = 0;
		currentLine++;
	}

	public void println(String str) {
		print(str);
		newline();
	}

	public void printOpenPunct(String punct, OutputContext context) {
		if (punct.equals("{")) {
			BracePosition newLine;

			//
			// Get the brace position
			//
			switch (context) {
			case TYPE_DEF:
				newLine = options.getBracePolicy().getTypeDef();
				break;

			case BLOCK:
			default:
				newLine = options.getBracePolicy().getBlock();
				break;
			}

			//
			// Perform the correct formatting
			//
			switch (newLine) {
			case NEXT_LINE:
				newline();
				break;

			case SAME_LINE:

				break;
			}
		}

		print(punct);

		if (punct.equals("{") && context != OutputContext.OBJECT_LITERAL) {
			newline();
		}
	}

	public void printClosePunct(String punct, OutputContext context) {
		print(punct);

		if (punct.equals("}") && context != OutputContext.OBJECT_LITERAL) {
			newline();
		}
	}

	public void printOperator(String op, OutputContext context) {
		boolean before = false, after = false;

		switch (context) {
		case ASSIGN:
			before = options.getWhitespacePolicy().isBeforeAssign();
			after = options.getWhitespacePolicy().isAfterAssign();
			break;

		case INFIX:
			before = options.getWhitespacePolicy().isBeforeBinary();
			after = options.getWhitespacePolicy().isAfterBinary();
			break;

		case PREFIX:
			before = options.getWhitespacePolicy().isBeforePrefix();
			after = options.getWhitespacePolicy().isAfterPrefix();
			break;

		case POSTFIX:
			before = options.getWhitespacePolicy().isBeforePostfix();
			after = options.getWhitespacePolicy().isAfterPostfix();
			break;
		}

		if (before)
			print(" ");
		print(op);
		if (after)
			print(" ");
	}

	public void setCurrentLine(int line) {
		if (line <= currentLine) {
			return;
		}

		for (int i = currentLine; i < line; i++) {
			newline();
		}
	}

	public void increaseTabDepth() {
		tabDepth++;
	}

	public void decreaseTabDepth() {
		tabDepth--;
	}

	// ===== WARNINGS

	private void addWarning(int line, String message) {

	}

	// ===== VISITOR

	public boolean visit(ASArrayAccess type) {
		addWarning(type.getFullRegion().getLine(), "Array access used. "
				+ "haXe does not support object access using array "
				+ "style. Please ensure you are not.");
		type.getArray().accept(this, true);
		printOpenPunct("[", OutputContext.ARRAY_ACCESS);
		type.getIndex().accept(this, true);
		printClosePunct("]", OutputContext.ARRAY_ACCESS);
		return false;

	}

	public boolean visit(ASArrayLiteral type) {
		printOpenPunct("[", OutputContext.ARRAY_LITERAL);
		Iterator<ASTNode> itr = type.getElements().iterator();
		while (itr.hasNext()) {
			itr.next().accept(this, true);
			if (itr.hasNext()) {
				print(", ");
			}
		}
		printClosePunct("]", OutputContext.ARRAY_LITERAL);
		return false;
	}

	public boolean visit(ASAssignment type) {
		type.getLeftOperand().accept(this, true);
		printOperator(type.getOperator().token, OutputContext.ASSIGN);
		type.getRightOperand().accept(this, true);
		return false;

	}

	public boolean visit(ASBlock type) {
		printOpenPunct("{", OutputContext.BLOCK);
		increaseTabDepth();
		for (ASTNode stmt : type.getStatements()) {
			stmt.accept(this, true);
		}
		decreaseTabDepth();
		printClosePunct("}", OutputContext.BLOCK);
		return false;

	}

	public boolean visit(ASBooleanLiteral type) {
		print(type.getBooleanValue() + "");
		return false;

	}

	public boolean visit(ASBreakStatement type) {
		// do nothing
		return false;

	}

	public boolean visit(ASCatchStatement type) {
		print("catch ");
		printOpenPunct("(", OutputContext.CATCH);
		type.getException().accept(this, true);
		printClosePunct(")", OutputContext.CATCH);
		if (!(type.getBody() instanceof ASBlock)) {
			increaseTabDepth();
			type.getBody().accept(this, true);
			decreaseTabDepth();
		} else {
			type.getBody().accept(this, true);
		}

		return false;

	}

	public boolean visit(ASConditionalExpression type) {
		print("if ");
		printOpenPunct("(", OutputContext.CONDITIONAL);
		type.getExpression().accept(this, true);
		printClosePunct(")", OutputContext.CONDITIONAL);
		type.getThenExpression().accept(this, true);
		print(" else ");
		type.getElseExpression().accept(this, true);
		return false;

	}

	public boolean visit(ASComment type) {
		print(type.getText());
		return false;
	}

	public boolean visit(ASContinueStatement type) {
		print("continue;");
		return false;

	}

	public boolean visit(ASDoStatement type) {
		println("do ");
		if (!(type.getBody() instanceof ASBlock)) {
			increaseTabDepth();
			type.getBody().accept(this, true);
			decreaseTabDepth();
		} else {
			type.getBody().accept(this, true);
		}
		println("while");
		printOpenPunct("(", OutputContext.DO);
		type.getExpression().accept(this, true);
		printClosePunct(")", OutputContext.DO);
		print(";");
		return false;

	}

	public boolean visit(ASExpressionStatement type) {
		type.getExpression().accept(this, true);
		println(";");
		return false;

	}

	public boolean visit(ASFieldAccess type) {
		print(ASName.valueOf(type).toString());
		return false;

	}

	public boolean visit(ASForInStatement type) {
		print("for ");
		printOpenPunct("(", OutputContext.FOR);
		type.getKeyVariable().accept(this, true);
		print(" in ");
		print("Reflect.fields");
		printOpenPunct("(", OutputContext.FUNCTION_CALL);
		type.getExpression().accept(this, true);
		printClosePunct(")", OutputContext.FUNCTION_CALL);
		printClosePunct(")", OutputContext.FOR);
		if (!(type.getBody() instanceof ASBlock)) {
			increaseTabDepth();
			type.getBody().accept(this, true);
			decreaseTabDepth();
		} else {
			type.getBody().accept(this, true);
		}

		return false;
	}

	public boolean visit(ASForStatement type) {
		print("var ");
		type.getInitializers().accept(this, true);
		println(";");
		print("while ");
		printOpenPunct("(", OutputContext.FOR);
		type.getExpression().accept(this, true);
		printClosePunct(")", OutputContext.FOR);
		if (!(type.getBody() instanceof ASBlock)) {
			printOpenPunct("{", OutputContext.BLOCK);
			increaseTabDepth();
			type.getBody().accept(this, true);
			type.getUpdaters().accept(this, true);
			decreaseTabDepth();
			printClosePunct("}", OutputContext.BLOCK);
		} else {
			((ASBlock) type.getBody())
					.getStatements().add(
							new ASExpressionStatement(type.getAST(), type
									.getUpdaters()));
			type.getBody().accept(this, true);
		}

		return false;

	}

	public boolean visit(ASFunction type) {
		if (type.getModifiers() != ASModifier.NONE) {
			print(type.isPrivate() ? "private " : "public ");
			if (type.isStatic()) {
				print("static ");
			}
		}
		print("function ");

		//
		// FIXME renaming should be in the transformer
		//
		if (type.getName() != null) {
			if (type.isConstructor()) {
				print("new");
			} else if (type.isGetter()) {
				print(GETTER_PREFIX);
				print(type.getName().toString());
			} else if (type.isSetter()) {
				print(SETTER_PREFIX);
				print(type.getName().toString());
			} else {
				print(type.getName().toString());
			}
		}
		printOpenPunct("(", OutputContext.FUNCTION_DEF);
		Iterator<ASTNode> itr = type.getParameters().iterator();
		while (itr.hasNext()) {
			itr.next().accept(this, true);

			if (itr.hasNext()) {
				print(", ");
			}
		}
		printClosePunct(")", OutputContext.FUNCTION_DEF);

		if (type.getReturnType() != null) {
			print(":");
			print(type.getReturnType().getName().toString());
		}

		if (type.getBody() != null) {
			print(" ");
			type.getBody().accept(this, true);
		} else {
			println(";");
		}

		return false;
	}

	public boolean visit(ASFunctionExpression type) {
		type.getFunction().accept(this, true);
		print(";");
		return false;
	}

	public boolean visit(ASIfStatement type) {

		print("if ");
		printOpenPunct("(", OutputContext.IF);
		type.getExpression().accept(this, true);
		printClosePunct(")", OutputContext.IF);
		type.getThenStatement().accept(this, true);

		if (type.getElseStatement() != null) {
			print("else ");
			type.getElseStatement().accept(this, true);
		}

		newline();
		return false;
	}

	public boolean visit(ASImportDeclaration type) {
		print("import ");
		print(type.getName().toString());
		println(";");
		return false;

	}

	public boolean visit(ASInfixExpression type) {
		type.getLeftOperand().accept(this, true);
		printOperator(type.getOperator().token, OutputContext.INFIX);
		type.getRightOperand().accept(this, true);

		return false;
	}

	public boolean visit(ASInstanceOfExpression type) {
		print("Std.is");
		printOpenPunct("(", OutputContext.FUNCTION_CALL);
		type.getLeftOperand().accept(this, true);
		print(", ");
		type.getRightOperand().accept(this, true);
		printClosePunct(")", OutputContext.FUNCTION_CALL);
		return false;
	}

	public boolean visit(ASMethodInvocation type) {
		if (type.isCast()) {
			print("cast");
			printOpenPunct("(", OutputContext.FUNCTION_CALL);
			type.getArguments().get(0).accept(this, true);
			print(", ");
			type.getExpression().accept(this, true);
			printClosePunct(")", OutputContext.FUNCTION_CALL);
		} else {
			type.getExpression().accept(this, true);
			printOpenPunct("(", OutputContext.FUNCTION_CALL);
			Iterator<ASTNode> itr = type.getArguments().iterator();
			while (itr.hasNext()) {
				itr.next().accept(this, true);
				if (itr.hasNext()) {
					print(", ");
				}
			}
			printClosePunct(")", OutputContext.FUNCTION_CALL);
		}
		return false;
	}

	public boolean visit(ASName type) {
		print(type.toString());
		return false;

	}

	public boolean visit(ASNewExpression type) {
		print("new ");
		type.getConstructorCall().accept(this, true);
		return false;
	}

	public boolean visit(ASNullLiteral type) {
		print("null");
		return false;

	}

	public boolean visit(ASNumberLiteral type) {
		print(type.getNumberValue().toString());
		return false;

	}

	public boolean visit(ASObjectLiteral type) {
		printOpenPunct("{", OutputContext.OBJECT_LITERAL);

		Iterator<ASTNode> itr = type.getFields().iterator();
		while (itr.hasNext()) {
			itr.next().accept(this, true);
			if (itr.hasNext()) {
				print(", ");
			}
		}

		printClosePunct("}", OutputContext.OBJECT_LITERAL);
		return false;
	}

	public boolean visit(ASObjectLiteralField type) {
		print(type.getName().toString());
		print(" : ");
		type.getValue().accept(this, true);
		return false;

	}

	public boolean visit(ASPackage type) {
		//
		// Don't output if empty
		//
		if (!shouldOutputPackage(type)) {
			return false;
		}

		if (log.isInfoEnabled()) {
			if (type.getName() == null) {
				log.info("Writing root package");
			} else {
				log.info("Writing package: " + type.getName().toString());
			}
		}

		File dir = packageDirs.get(type);
		if (dir == null) {
			if (type.getParent() == null) {
				dir = outputDir;
			} else {
				File parentDir = packageDirs.get((ASPackage) type.getParent());
				ASSimpleName pkgName = type.getName() instanceof ASSimpleName ? (ASSimpleName) type
						.getName()
						: ((ASQualifiedName) type.getName()).getName();
				dir = new File(parentDir, pkgName.toString());
				dir.mkdir();
			}

			packageDirs.put(type, dir);
		}

		return true;
	}

	/**
	 * Returns whether the package should be outputted
	 * 
	 * @param type
	 * @return
	 */
	private boolean shouldOutputPackage(ASPackage type) {
		if (type.isEmpty()) {
			return false;
		}

		for (ASTypeDeclaration t : type.getTypesMap().values()) {
			if (!t.getExtraInfo().containsKey(Constants.PROP_DO_NOT_OUTPUT)) {
				return true;
			}
		}

		for (ASPackage pkg : type.getPackagesMap().values()) {
			if (shouldOutputPackage(pkg)) {
				return true;
			}
		}

		return false;
	}

	public boolean visit(ASParenthesizedExpression type) {
		printOpenPunct("(", OutputContext.EXPRESSION);
		type.getExpression().accept(this, true);
		printClosePunct(")", OutputContext.EXPRESSION);
		return false;

	}

	public boolean visit(ASPostfixExpression type) {
		type.getOperand().accept(this, true);
		printOperator(type.getOperator().token, OutputContext.POSTFIX);

		return false;

	}

	public boolean visit(ASPrefixExpression type) {
		printOperator(type.getOperator().token, OutputContext.PREFIX);
		type.getOperand().accept(this, true);
		return false;

	}

	public boolean visit(ASPropertyDeclaration type) {
		print("public var ");
		print(type.getName().toString());
		printOpenPunct("(", OutputContext.PROPERTY);
		if (type.getGetter() != null) {
			print(GETTER_PREFIX + type.getGetter().getName().toString());
		} else {
			print("null");
		}
		print(", ");
		if (type.getSetter() != null) {
			print(SETTER_PREFIX + type.getSetter().getName().toString());
		} else {
			print("null");
		}
		printClosePunct(")", OutputContext.PROPERTY);
		print(":");
		print(type.getType().toString());
		println(";");

		return false;
	}

	public boolean visit(ASReturnStatement type) {
		print("return ");
		if (type.getExpression() != null) {
			type.getExpression().accept(this, true);
		}
		println(";");
		return false;

	}

	public boolean visit(ASStringLiteral type) {
		print(type.getStringValue());
		return false;
	}

	public boolean visit(ASSwitchCase type) {
		print("case ");
		type.getExpression().accept(this, true);
		println(":");
		for (ASTNode stmt : type.getStatements()) {
			stmt.accept(this, true);
		}

		newline();
		return false;

	}

	public boolean visit(ASSwitchStatement type) {
		print("switch ");
		printOpenPunct("(", OutputContext.SWITCH);
		type.getExpression().accept(this, true);
		printClosePunct(")", OutputContext.SWITCH);
		printOpenPunct("{", OutputContext.BLOCK);
		for (ASTNode cse : type.getCases()) {
			cse.accept(this, true);
		}
		printClosePunct("}", OutputContext.BLOCK);
		newline();
		return false;
	}

	public boolean visit(ASThrowStatement type) {
		print("throw ");
		type.getExpression().accept(this, true);
		print(";");
		newline();
		return false;

	}

	public boolean visit(ASTryStatement type) {
		print("try ");
		type.getBody().accept(this, true);

		for (ASTNode ctch : type.getCatches()) {
			ctch.accept(this, true);
		}

		newline();
		return false;
	}

	public boolean visit(ASType type) {
		if (type.getName().toString().equals("Array")) {
			print("Array<Dynamic>");
		} else {
			print(type.getName().toString());
		}

		return false;

	}

	/**
	 * Creates the necessary files, and writes out the type.
	 */
	public boolean visit(ASTypeDeclaration type) {
		//
		// Check the properties for do not output flag
		//
		if (type.getExtraInfo().containsKey(Constants.PROP_DO_NOT_OUTPUT)) {
			return false;
		}

		//
		// Log
		//
		if (log.isInfoEnabled()) {
			log.info("Writing type: " + type.getName().toString());
		}

		//
		// Create the file
		//
		performTypeSetup(type);

		//
		// Print out package name
		// 
		ASCompilationUnit compilationUnit = (ASCompilationUnit) type
				.getParent();
		if (compilationUnit.getPackageDeclaration().getName() != null) {
			println("package "
					+ compilationUnit
							.getPackageDeclaration().getName().toString() + ";");
			newline();
		}

		//
		// Print out imports
		//
		for (ASTNode imp : compilationUnit.getImports()) {
			imp.accept(this, true);
		}

		newline();

		//
		// Print out everything before the header
		//
		// List<ASTNode> preHeaderComments = type.getCodeRegions(0, type
		// .getFullRegion().getOffset());
		// for (ASTNode comment : preHeaderComments) {
		// comment.accept(this, true);
		// }

		//
		// Print out the type declaration
		//
		printTypeHeader(type);

		//
		// Get the code regions
		//
		List<ASTNode> regions = type.getCodeRegions(type
				.getFullRegion().getOffset(), -1);

		//
		// Type body
		//
		printOpenPunct("{", OutputContext.TYPE_DEF);
		increaseTabDepth();

		int lineOfLastRegion = -1;

		for (ASTNode region : regions) {
			int line = region.getFullRegion().getLine();

			if (lineOfLastRegion != -1) {
				int jumpBy = line - lineOfLastRegion;
				setCurrentLine(currentLine + jumpBy);
			}

			region.accept(this, true);

			lineOfLastRegion = line + region.getFullRegion().getNumberOfLines();
		}

		// for (ASVariableDeclarationStatement var : type.getVariables()) {
		// var.accept(this, true);
		// }
		//
		// for (ASFunction func : type.getFunctions()) {
		// func.accept(this, true);
		// }

		decreaseTabDepth();
		newline();
		printClosePunct("}", OutputContext.TYPE_DEF);

		// 
		// Write the file
		//
		performTypeCleanup(type);

		return false;
	}

	/**
	 * Prints out the type header.
	 * 
	 * @param type
	 *            the type
	 */
	private void printTypeHeader(ASTypeDeclaration type) {
		newline();

		//
		// interface / type
		//
		if (type.isInterface()) {
			print("interface ");
		} else {
			print("class ");
		}

		//
		// Name
		//
		print(type.getName().getName().toString());

		//
		// Super type
		//
		if (type.getSuperClassType() != null) {
			print(" extends ");
			print(type.getSuperClassType().getName().toString());
			if (!type.getInterfaceTypes().isEmpty()) {
				print(",");
			}
		}

		//
		// Interfaces
		//
		if (!type.getInterfaceTypes().isEmpty()) {
			boolean first = true;
			for (ASTNode interfc : type.getInterfaceTypes()) {
				if (first) {
					first = !first;
				} else {
					print(",");
				}

				print(" implements ");
				print(((ASType) interfc).getName().toString());
			}
		}
	}

	/**
	 * Performs the necessary type setup
	 * 
	 * @param type
	 *            the type
	 */
	private void performTypeSetup(ASTypeDeclaration type) {
		ASPackageDeclaration pkg = ((ASCompilationUnit) type.getParent()).getPackageDeclaration();
		File dir = packageDirs.get(pkg);
		currentFile = new File(dir, type.getName().getName().toString() + ".hx");
		try {
			currentFile.createNewFile();
		} catch (IOException e) {
			// TODO do something
		}

		currentBuffer = new StringBuffer();
		currentLinePosition = 0;
	}

	public void performTypeCleanup(ASTypeDeclaration type) {
		try {
			BufferedWriter writer = new BufferedWriter(new FileWriter(
					currentFile));
			writer.write(currentBuffer.toString());
			writer.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public boolean visit(ASUndefinedLiteral type) {
		print("null");
		return false;
	}

	public boolean visit(ASVariableDeclaration type) {
		print(type.getName().toString());

		if (type.getType() != null
				&& !type.getType().toString().equals("Function")) { // Function
			// check
			print(":");
			type.getType().accept(this, true);
		}

		if (type.getInitializer() != null) {
			printOperator("=", OutputContext.ASSIGN);
			type.getInitializer().accept(this, true);
		}

		return false;
	}

	public boolean visit(ASVariableDeclarationExpression type) {
		type.getDeclaration().accept(this, true);
		return false;
	}

	public boolean visit(ASVariableDeclarationStatement type) {

		boolean isPrivate = type.getDeclaration().isPrivate();
		boolean isStatic = type.getDeclaration().isStatic();
		if (type.getDeclaration().getModifiers() != ASModifier.NONE) {
			print(isPrivate ? "private" : "public");
			print(" ");
			if (isStatic) {
				print("static ");
			}
		}
		print("var ");
		type.getDeclaration().accept(this, true);
		print(";");

		if (!(type.getParent() instanceof ASTypeDeclaration)) {
			newline();
		}

		return false;
	}

	public boolean visit(ASWhileStatement type) {
		print("while");
		printOpenPunct("(", OutputContext.WHILE);
		type.getExpression().accept(this, true);
		printClosePunct(")", OutputContext.WHILE);
		type.getBody().accept(this, true);
		newline();
		return false;

	}

	public boolean visit(ASWithStatement type) {
		return true;

	}

	public boolean visit(IVisitable type) {
		return true;

	}
}
